package pdf解析.config;

import org.apache.ibatis.io.ResolverUtil;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import pdf解析.annotation.RpcClass;
import pdf解析.annotation.RpcMethod;
import pdf解析.service.AopService;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * rpc配置处理
 *
 * @author caihe
 * @date 2022/3/15 15:14
 */
@Component
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //这里我为了简单起见，直接写了2个固定接口，一般我们是通过反射获取需要代理的接口的clazz列表
        //比如判断包下面的类，或者通过某注解标注的类等等
//        List<Class<?>> beanClazzs = Arrays.asList(AopService.class);
        
//        // 参考mybatis的扫描操作
//        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//        resolverUtil.find(new ResolverUtil.IsA(BaseApi.class), ProjectInfoUtils.BASE_PACKAGE_PREFIX);
//        Set<Class<? extends Class<?>>> classes = resolverUtil.getClasses();
//
//        List<Class<? extends Class<?>>> beanClazzList = resolverUtil.getClasses().stream()
//                .filter(item -> item.isAnnotationPresent(RpcFeign.class)).collect(Collectors.toList());


        // 通过注解获取所有被注解的API接口
        String scanPackage = "pdf解析";
        Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(scanPackage)).setScanners(new MethodAnnotationsScanner()));
        Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(RpcClass.class);


        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
        resolverUtil.find(new ResolverUtil.IsA(Object.class), "pdf解析");
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        List<Class<? extends Class<?>>> beanClazzs = mapperSet.stream().filter(item -> item.isAnnotationPresent(RpcMethod.class)).collect(Collectors.toList());


        for (Class beanClazz : beanClazzs) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();

            //在这里，我们可以给该对象的属性注入对应的实例。
            //比如mybatis，就在这里注入了dataSource和sqlSessionFactory，
            // 注意，如果采用definition.getPropertyValues()方式的话，
            // 类似definition.getPropertyValues().add("interfaceType", beanClazz);
            // 则要求在FactoryBean（本应用中即ServiceFactory）提供setter方法，否则会注入失败
            // 如果采用definition.getConstructorArgumentValues()，
            // 则FactoryBean中需要提供包含该属性的构造方法，否则会注入失败
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);

            //注意，这里的BeanClass是生成Bean实例的工厂，不是Bean本身。
            // FactoryBean是一种特殊的Bean，其返回的对象不是指定类的一个实例，
            // 其返回的是该工厂Bean的getObject方法所返回的对象。
            definition.setBeanClass(ServiceFactory.class);

            //这里采用的是byType方式注入，类似的还有byName等
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
